from __future__ import division
from pygame import Surface, Rect, RLEACCEL
import pygame
import image
import imagecache
import constants
import colour
from link import Link


# Because this object caches its images, self.changed must be set to True if the contents of it change to update the image.
class Map:
    def __init__(self, width, height, ground_image_path):
        self.width = width
        self.height = height
        self.ground_image_path = ground_image_path
        self.ground_image = self.form_ground_image()
        self.items = list()
        
        self.x_offset = 0
        self.y_offset = 0
        
        self.outside = True
        self.last_hour = -1 # only used if self.outside is True; a number larger than the highest hour
        
        self.image = None
        self.changed = True
    
    def add_item(self, item, (x,y)):
        self.items.append(PlacedObject(item, (x,y)))
        self.changed = True
    
    # get the PlacedItem of the item; returns None if the item isn't in this map.
    def get_item(self, item):
        for i in self.items:
            if i.object is item:
                return i
        return None
    
    # get all the items who are at the given point
    def get_items(self, (x,y)):
        ret = list()
        for i in self.items:
            if i.get_rect().collidepoint((x,y)):
                ret.append(i)
        return ret
    
    # get the link at the specified point; if the map is correctly formed, there should only be one link at any point
    def get_link(self, (x,y)):
        for i in self.get_items((x,y)):
            if isinstance(i.object, Link):
                return i.object
        return None
    
    # amazingly, i think this actually works
    def remove_item(self, item):
        [self.items.remove(item) for item in filter(lambda x: x.object == item, self.items)]
        self.changed = True
    
    # returns True if all points in the specified rectangle are walkable
    def can_walk_on(self, rect_coordinates):
        unwalkables = filter(lambda x: not x.object.walkable() ,self.items)
        for i in unwalkables:
            object_rect = Rect(i.position[0], i.position[1], i.object.get_image().get_rect().width, i.object.get_image().get_rect().height)
            player_rect = Rect(rect_coordinates)
            if object_rect.colliderect(player_rect):
                return False
        return True
    
    def end_turn(self):
        # call end_turn() on every item; catch the exception if it doesn't have that method
        # there really should be a better way to do this
        for i in self.items:
            try:
                i.object.end_turn()
            except(AttributeError):
                pass
        self.changed = True
    
    # set a rectangle to be the specified image
    def set_ground(self, (x, y), width, height, image_path):
        new_ground = image.tile_image(imagecache.get_image(image_path), width, height)
        self.ground_image.blit(new_ground, (x, y))
        self.changed = True
    
    def interact(self, (x, y), game):
        for i in self.get_items((x, y)):
            i.object.interact(game)
        self.changed = True
    
    # called to remake the image
    def form_image(self, hour=12):
        image = Surface((self.width, self.height))
        image.blit(self.ground_image, (0, 0))
        
        for item in self.items:
            image.blit(item.object.get_image(), (item.position[0], item.position[1]))
        
        if self.outside:
            self.last_hour = hour
            image.blit(self.time_overlay(hour), (0,0))
        
        self.image = image
        self.changed = False
    
    # remakes the ground, which will destroy any changes made after this items initialisation
    def form_ground_image(self):
        if 1==0 and self.width < constants.x_res or self.height < constants.y_res:
            ground_image = Surface((constants.x_res, constants.y_res))
            self.x_offset = (constants.x_res - self.width) // 2
            self.y_offset = (constants.y_res - self.height) // 2
            ground_image.blit(image.tile_image(imagecache.get_image(self.ground_image_path), self.width, self.height), (self.x_offset, self.y_offset))
        ground_image = image.tile_image(imagecache.get_image(self.ground_image_path), self.width, self.height)
        return ground_image
    
    # create a layer for showing how dark it is (nighttime is darker)
    def time_overlay(self, hour):    
        time_overlay = Surface((self.width, self.height))
        if hour < 8:
            time_overlay.fill(colour.BLUE)
            time_overlay.set_alpha(8*(8-hour))
        elif hour < 12:
            time_overlay.set_alpha(0)
        elif hour < 20:
            time_overlay.fill(colour.ratio(colour.ORANGE, colour.BLUEBLACK, (hour-12)/8))
            time_overlay.set_alpha(16 + 128 - ((24-hour) / 8 * 128))
        elif hour < 24:
            time_overlay.fill(colour.BLUEBLACK)
            time_overlay.set_alpha(96)
        else:
            time_overlay.set_alpha(0)
            
        return time_overlay

    
    def get_image(self, (x, y)=(0, 0), width=constants.x_res, height=constants.y_res, hour=12):
        if self.changed or hour > self.last_hour:
            self.form_image(hour=hour)
            self.changed = False            
        
        # make sure width, height are in range
        x %= self.width
        y %= self.height
        
        ret = self.image.subsurface((x, y, min(width, self.image.get_width()-x), min(height, self.image.get_height()-y)))
        return ret
    
    
class PlacedObject:
    def __init__(self, object, (x, y)):
        self.object = object
        self.position = (x, y)
    
    def get_rect(self):
        return Rect(self.position[0], self.position[1], self.object.get_image().get_rect().width, self.object.get_image().get_rect().height)
    
    def get_relative_position(self, (x, y)):
        return (x - self.position[0], y - self.position[1])
    
    def __str__(self):
        return str(self.object)
        
# "item" for restricting movement; has a blank picture
class Blank:
    def __init__(self, width, height):
        self.image = Surface((width, height))
        self.image.fill(colour.BLACK)
        #self.image = image.tile_image(imagecache.get_image('blank'), width, height)
    
    def walkable(self):
        return False
    
    def get_image(self):
        return self.image
    
    def interact(self):
        pass